#ifndef __XML_NODE_H__
  #define __XML_NODE_H__

#include <string>
#include <list>

#include "safe_pointer.h"

using namespace std;

struct xml_attribute {
  string name; // attribute name
  string content; // attribute content

  xml_attribute(const string &name, const string &content) : name(name), content(content) { /* nothing to do */ }
}; // struct xml_attribute

//! XML node representation

/*!
This class represents an XML node. An XML node can contain many attributes and many children.
Every node must have a name and some content or it can be empty. Every xml_node will delete all of
its children & their attributes when it goes out of scope (destructor call).

Implementation date: 2008

Usage:

\code
xml_node xn;
xn.set_name("root_tag");
xn.set_content("I'm the root tag.");

xn.add_attribute(new xml_attribute("Id", "17")); // adding a new attribute (the allocated memory will be freed by xml_node)
xn.add_child(new xml_node("child1", "I'm a child node. My father is the 'root'.")); // adding a new child (the allocated memory will be freed by xml_node)

string s1;
xn.to_string(s1); // string representation

cout << s1 << endl; // will print "<root_tag Id="17">I'm the root tag.<child1>I'm a child node. My father is the 'root'.</child1></root_tag>"
cout << xn.get_name() << endl;
cout << xn.get_content() << endl;
\endcode
*/

class xml_node {
friend class xml_parser;

private:
  list<xml_attribute*> attributes;
  list<xml_node*> children;

  string name; // tag name
  string content; // XML content

  static string opening_tag; // default is '<', but for example it can be changed to "&lt;" (HTML compatible)
  static string closing_tag; // default is '>', but for example it can be changed to "&gt;" (HTML compatible)

public:
  //! sets the opening tag to the given string. for example "&lt;" to make the XML string HTML comptabile
  static void set_opening_tag(const string &tag = "<");

  //! sets the closing tag to the given string. for example "&gt;" to make the XML string HTML comptabile
  static void set_closing_tag(const string &tag = ">");

  enum destruction_type { destroy_all = 0, destroy_attributes, destroy_children };
  enum search_element { search_attributes = 0, search_children };

  //! flexible search operator, it never throws an exception, if the str_search is not found, then an empty string will be returned (for attributes: excluding sub-children, for node: including sub-children)
  const string &operator()(search_element se, bool case_sensitive, const string &str_search) const;

  //! returns the content of the given attribute, throws an exception if the attribute is not found (excluding sub-children)
  const string &get_attribute_content(const string &attribute_name, bool case_sensitive = true) const;

  //! searches the current xml_node and all of its children for the given name and returns the found node or it will return null
  /*!
  this is a very simple implementation of xpath-like syntax. example:
  safe_pointer<xml_node> sp_xn = xn.search_by_path("site/open_auctions");
  */
  const safe_pointer<xml_node> search_by_path(const string &path) const;

  //! searches within all children of this node for the given name and returns the found node or it will return null (including sub-children : [recursive = true])
  const safe_pointer<xml_node> search_by_tag_name(const string &tag_name, bool case_sensitive = true, bool recursive = true) const;

  //! searches within all attributes of this node for the given name and returns the found attribute or it will return null (excluding sub-children)
  const safe_pointer<xml_attribute> search_by_attribute_name(const string &attribute_name, bool case_sensitive = true) const;

  //! searches within all children of this node for the given name and removes the found node or it will do nothing (returns false if nothing is deleted) (including sub-children : [recursive = true])
  bool remove_by_tag_name(const string &tag_name, bool case_sensitive = true, bool recursive = true);

  //! searches within all attributes of this node for the given name and removes the found attribute or it will do nothing (returns false if nothing is deleted) (excluding sub-children)
  bool remove_by_attribute_name(const string &attribute_name, bool case_sensitive = true);

  //! returns all attributes of this node in string format
  void get_all_attributes(string &output) const;

  //! returns all children of this node including their attributes in string format
  void get_all_children(string &output, bool pretty_print, bool include_attributes, const string &spacer) const;

  //! sets the content of this node to the given string
  void set_content(const string &content);

  //! returns the content of this xml_node
  const string &get_content() const;

  //! sets the tag name of this xml_node
  void set_name(const string &name);

  //! returns the tag name of this xml_node
  const string &get_name() const;

  //! returns a list containing all attributes of this xml_node
  const list<xml_attribute*> &get_attributes() const;

  //! returns a list containing all children of this xml_node
  const list<xml_node*> &get_children() const;

  //! returns the string representation of this xml_node including its children and their attributes
  void to_string(string &output, bool pretty_print = false, bool include_attributes = true, const string &spacer = "  ") const;

  //! adds a new child to this xml_node. the ownership of xn will be transferred to this xml_node and will be deleted by this xml_node
  void add_child(xml_node *xn);

  //! add a new attribute to this xml_node. the ownership of xa will be transferred to this xml_node and will be deleted by this xml_node
  void add_attribute(xml_attribute *xa);

  //! deletes all children & attributes
  void destroy(destruction_type dt = destroy_all);

  //! assignment operator
  xml_node &operator=(const xml_node &xn);

  //! copy constructor
  xml_node(const xml_node &xn);

  //! default constructor
  xml_node();

  //! constructor
  xml_node(const string &name, const string &content);

  //! destructor (non-virtual: please do not inherit from this class)
  ~xml_node();
}; // class xml_node

#endif
